﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using TLib.Xna;

namespace COMP477_Scorchery
{
    enum ParticleType
    {
        Fire,
    }

    class ParticleSystem3D
    {
        public ParticleType ParticleSystemType
        {
            get { return particleSystemType; }
            set
            {
                particleSystemType = value;
                switch (particleSystemType)
                {
                    case ParticleType.Fire:
                        MAX_PARTICLES = 150;
                        EmissionRate = 0.1f;
                        break;
                    default:
                        break;
                }
            }
        }
        private ParticleType particleSystemType;

        private GraphicsDevice graphicsDevice;

        public Texture2D[] ParticleTextures;

        public List<Particle3D> ParticleList;
        public int MAX_PARTICLES;
        public float EmissionRate;
        public float CurrentTime;
        public bool IsEmitting;

        public Vector3 Position;
        public Vector3 Target;
        public float Length;
        public float Width;
        public float Height;

        public int RedMin;
        public int RedMax;
        public int GreenMin;
        public int GreenMax;
        public int BlueMin;
        public int BlueMax;

        private Random random;

        protected DynamicVertexBuffer vertexBuffer;
        protected IndexBuffer indexBuffer;
        protected List<BasicEffect> basicEffects;

        public ParticleSystem3D(GraphicsDevice graphicsDevice)
        {
            this.graphicsDevice = graphicsDevice;

            ParticleList = new List<Particle3D>();

            IsEmitting = true;
            
            random = new Random((int)DateTime.Now.Ticks);

            Position = new Vector3(0, 0, 0);
            Target = new Vector3(0, 1, 0);
            Length = 3;
            Width = 0.25f;
            Height = -0.25f;

            RedMin = 225;
            RedMax = 255;
            GreenMin = 75;
            GreenMax = 175;
            BlueMin = 0;
            BlueMax = 0;

            vertexBuffer = new DynamicVertexBuffer(graphicsDevice,
                                                   typeof(VertexPositionTexture),
                                                   2000, BufferUsage.None);
            indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
                                          3000, BufferUsage.None);

            basicEffects = new List<BasicEffect>();

            ParticleSystemType = ParticleType.Fire;
            CurrentTime = EmissionRate;
        }

        public void LoadContent(ContentManager content)
        {
            ParticleTextures = new Texture2D[1];
            ParticleTextures[0] = content.Load<Texture2D>("Graphics\\fire");
        }

        public void Update(GameTime gameTime, GameObject camera)
        {
            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Target = Position + new Vector3(0, 5, 0);// +new Vector3(0, 2, random.Next((int)-Width / 4, (int)Width / 4));


            foreach (Particle3D p in ParticleList)
            {
                p.Update(gameTime, camera);

                if (p.Age >= p.LifeSpan)
                {
                    p.Init();
                    double newTheta = random.NextDouble() * Math.PI * 2;
                    //p.Position = Position + new Vector2((float)Math.Sin(newTheta) * (Width / 2), (float)Math.Cos(newTheta) * (Height / 2));
                    //p.Position = Position + new Vector2((float)Math.Sin(newTheta) * random.Next((int)-Width / 2, (int)Width / 2), (float)Math.Cos(newTheta) * random.Next((int)-Height / 2, (int)Height / 2));
                    p.Position = Position + new Vector3((float)Math.Sin(newTheta) * (float)random.NextDouble() * Width * 1.5f, (float)random.NextDouble() * Height, (float)Math.Cos(newTheta) * (float)random.NextDouble() * Width * 1.5f);
                    p.Direction = Target - p.Position;
                    p.RotationSpeed = random.Next(-2, 3);
                    p.Acceleration = random.Next(10, 10);
                    p.Velocity = Vector3.Zero;
                    p.Color = new Color(random.Next(RedMin, RedMax), random.Next(GreenMin, GreenMax), random.Next(BlueMin, BlueMax));
                    p.Scale = random.Next((int)(Width * 40), (int)(Width * 120)) * 0.01f;
                }
            }

            //Create new particles
            if (ParticleList.Count < MAX_PARTICLES && CurrentTime >= EmissionRate && IsEmitting)
            {
                switch (particleSystemType)
                {
                    case ParticleType.Fire:
                        Particle3D p = new FireParticle3D(ParticleTextures[0]);
                        double newTheta = random.NextDouble() * Math.PI * 2;
                        //p.Position = Position + new Vector2((float)Math.Sin(newTheta) * (Width / 2), (float)Math.Cos(newTheta) * (Height / 2));
                        p.Position = Position + new Vector3((float)Math.Sin(newTheta) * (float)random.NextDouble() * Width * 1.5f, (float)random.NextDouble() * Height, (float)Math.Cos(newTheta) * (float)random.NextDouble() * Width * 1.5f);
                        p.Color = new Color(random.Next(RedMin, RedMax), random.Next(GreenMin, GreenMax), random.Next(BlueMin, BlueMax));
                        p.Acceleration = random.Next(10, 10);
                        p.Direction = Target - p.Position;
                        p.LifeSpan = (Math.Abs(Height) * 0.4f) + 0.5f;
                        p.RotationSpeed = random.Next(-2, 3);
                        p.MAX_SPEED = Math.Abs(Height) * 45;
                        p.Scale = random.Next((int)(Width * 40), (int)(Width * 120)) * 0.01f;
                        ParticleList.Add(p);

                        BasicEffect be = new BasicEffect(graphicsDevice);
                        be.TextureEnabled = true;
                        be.LightingEnabled = true;
                        be.Texture = p.Texture;
                        basicEffects.Add(be);
                        break;
                    default:
                        break;
                }

                CurrentTime = 0;
            }

            CurrentTime += delta;
        }

        public void Draw(Matrix view, Matrix projection)
        {
            List<VertexPositionNormalTexture> vertices = new List<VertexPositionNormalTexture>();
            List<ushort> indices = new List<ushort>();

            for (int i = 0; i < ParticleList.Count; i++)
            {
                vertices.AddRange(ParticleList[i].vertices);

                indices.Add((ushort)(i * 4));
                indices.Add((ushort)(i * 4 + 2));
                indices.Add((ushort)(i * 4 + 1));

                indices.Add((ushort)(i * 4));
                indices.Add((ushort)(i * 4 + 3));
                indices.Add((ushort)(i * 4 + 2));
            }

            vertexBuffer = new DynamicVertexBuffer(graphicsDevice,
                                                   typeof(VertexPositionNormalTexture),
                                                   vertices.Count, BufferUsage.None);
            indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
                                          indices.Count, BufferUsage.None);

            vertexBuffer.SetData(vertices.ToArray());
            indexBuffer.SetData(indices.ToArray());

            graphicsDevice.SetVertexBuffer(vertexBuffer);
            graphicsDevice.Indices = indexBuffer;

            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

            

            for (int i = 0; i < ParticleList.Count; i++)
            {
                basicEffects[i].World = ParticleList[i].World;
                basicEffects[i].View = view;
                basicEffects[i].Projection = projection;

                basicEffects[i].Alpha = ParticleList[i].Alpha;
                basicEffects[i].AmbientLightColor = ParticleList[i].Color.ToVector3();

                foreach (EffectPass effectPass in basicEffects[i].CurrentTechnique.Passes)
                {
                    effectPass.Apply();
                } 

                graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, i * 4, 0,
                                                     4, 0, 2);
            }

            graphicsDevice.DepthStencilState = DepthStencilState.Default;
        }
    }
}
